package com.huiyun.project.pay.biz.handler.callback.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.ImmutableMap;
import com.huiyun.project.common.core.util.NumUtils;
import com.huiyun.project.common.sequence.IdUtils;
import com.huiyun.project.pay.biz.consts.PayOrderConst;
import com.huiyun.project.pay.biz.entity.MchNotify;
import com.huiyun.project.pay.biz.entity.PayChannel;
import com.huiyun.project.pay.biz.entity.PayOrder;
import com.huiyun.project.pay.biz.handler.callback.ICallbackHandler;
import com.huiyun.project.pay.biz.handler.check.impl.MessageRedisDuplicateCheckerHandler;
import com.huiyun.project.pay.biz.service.MchNotifyService;
import com.huiyun.project.pay.biz.service.PayChannelService;
import com.huiyun.project.pay.biz.service.PayOrderService;
import com.huiyun.project.pay.client.consts.MchNotifyConst;
import com.huiyun.project.pay.client.consts.PayConst;
import com.huiyun.project.pay.client.vo.NotifyResp;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Map;

/**
 * @author tiger
 */
@Component
@Slf4j
@AllArgsConstructor
public class AliPayCallbackHandler extends ICallbackHandler {

    private final PayOrderService payOrderService;

    private final MchNotifyService mchNotifyService;

    private final PayChannelService payChannelService;

    private final MessageRedisDuplicateCheckerHandler duplicateCheckerHandler;

    /**
     * 存储到redis和数据库,redis里面用于快速判断
     */
    @Override
    public void saveNotifyIfNotExist(NotifyResp notifyResp) {
        String mchOrderNo = notifyResp.getParams().get(PayConst.OUT_TRADE_NO);
        if (!duplicateCheckerHandler.isDuplicate("pay_notify:" + notifyResp.getMchId() + ":" + notifyResp.getMchOrderNo())) {
            PayChannel payChannel = payChannelService.getByMchIdAndChannelAppId(notifyResp.getMchId(), notifyResp.getChannelAppId());
            //存储到数据库,可用于重发,也可以理解为接收记录
            mchNotifyService.save(new MchNotify()
                    .setId(Convert.toStr(IdUtils.getId()))
                    .setMchId(notifyResp.getMchId())
                    .setChannelId(notifyResp.getChannelId())
                    .setCreateTime(new Date())
                    .setMchOrderNo(mchOrderNo)
                    .setNotifyCount(0)
                    //商家的通知地址
                    .setNotifyUrl(payChannel.getMchDomain())
                    .setStatus(MchNotifyConst.STATUS.IN.getValue())
                    .setType(MchNotifyConst.TYPE.PAY.getValue())
            );
        }
    }

    /**
     * 修改通知消息
     * uniqueKey 主键ID
     *
     * @param status
     * @return
     */
    @Override
    public Boolean updateNotify(String mchId, String mchOrderNo, Integer status, String result) {
        return mchNotifyService.updateNotifyById(ImmutableMap.of("mchId", mchId, "mchOrderNo", mchOrderNo, "result", result, "status", status));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void process(NotifyResp notifyResp) {
        try {
            String mchId = notifyResp.getMchId(), channelId = notifyResp.getChannelId();
            Map<String, String> params = notifyResp.getParams();
            //10000成功
            String code = params.get("code");
            String msg = params.get("msg");
            String mchOrderNo = params.get("out_trade_no");//商户订单号
            Double totalFee = Convert.toDouble(params.get("total_amount"));//订单金额,单位为元
            String transactionId = params.get("trade_no");//支付宝交易订单号
            Date now = new Date();
            /******存储记录,可以用于重新发送******/
            saveNotifyIfNotExist(notifyResp);
            /******修改支付订单**********/
            //获取支付订单
            PayOrder dbPayOrder = payOrderService.getOne(new LambdaQueryWrapper<PayOrder>()
                    .eq(PayOrder::getMchId, mchId)
                    .eq(PayOrder::getMchOrderNo, mchOrderNo)
                    .notIn(PayOrder::getStatus, CollUtil.newArrayList(PayOrderConst.STATUS.SUCCESS.getValue(), PayOrderConst.STATUS.COMPLETE.getValue())));
            Assert.notNull(dbPayOrder, "支付订单不存在,mchId:" + mchId + " mchOrderNo:" + mchOrderNo);
            //校验金额
            Assert.isTrue(NumUtils.eq(dbPayOrder.getAmount(), totalFee * 100), "金额校验失败,订单号:{},商户:{},渠道:{},微信金额:{},支付订单金额:{},支付订单货币单位:{}",
                    mchOrderNo, mchId, channelId, totalFee, dbPayOrder.getAmount(), dbPayOrder.getCurrency());
            //业务结果
            boolean isOk = "10000".equalsIgnoreCase(code);
            String errorMsg = msg;
            if (isOk) {
                //修改支付订单结果信息 // 0：订单生成，1：支付中，-1：支付失败，2：支付成功，3：业务处理完成，-2：订单过期
                dbPayOrder.setStatus(PayOrderConst.STATUS.SUCCESS.getValue());
                dbPayOrder.setPaySuccTime(now.getTime());
                dbPayOrder.setUpdateTime(now);
                //渠道订单编号
                dbPayOrder.setChannelOrderNo(transactionId);
                payOrderService.updateById(dbPayOrder);
            } else {
                //修改支付订单结果信息
                dbPayOrder.setStatus(PayOrderConst.STATUS.FAIL.getValue());
                dbPayOrder.setErrCode(code);
                dbPayOrder.setErrMsg(errorMsg);
                //渠道订单编号
                dbPayOrder.setChannelOrderNo(transactionId);
                payOrderService.updateById(dbPayOrder);
            }
            //修改发送成功
            mchNotifyService.update(new MchNotify().setStatus(MchNotifyConst.STATUS.SUCCESS.getValue()).setResult(code).setUpdateTime(new Date()),
                    new LambdaQueryWrapper<MchNotify>().eq(MchNotify::getMchId, mchId).eq(MchNotify::getMchOrderNo, mchOrderNo));
            //向用户发送支付成功消息,通过openId+mp工具包
        } catch (Exception ex) {
            String msg = ExceptionUtils.getStackTrace(ex);
            log.error(msg);
            updateNotify(notifyResp.getMchId(), notifyResp.getMchOrderNo(), MchNotifyConst.STATUS.FAIL.getValue(), ex.getMessage());
            //抛出异常,用于mq事务回滚
            Assert.isTrue(false, msg);
        }
    }
}
